package viz_main;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/***** 
*  A CAxis is a categorical axis. It contains a list of strings rather than a list of float values.
*  it is represented in a very similar way to other axis but with only discreet values along the axis
*  
*  Use getCategoryMap() in conjunction with getUniqueCategories() to determine the number of elements in each category
*  
*  Explanation: 
*  		Let's say we have a category "Setosa" that contains an array of pointers to every Setosa data case.
*  		the length of that array is equal to the number of Setosa in our data
*     	of course we will usually have more than 1 category:
*       	For every unique category in our data, we create a new array. 
*       	All of these arrays "Setosa", "Versicolor", and "Virginica" are then stored in a HashMap
*			An array can then be accessed very quickly simply by specifying the name of the category
*
*			categoricalArraysMap.get("Setosa");			// returns the Setosa array (an array if integers)
*			categoricalArraysMap.get("Setosa").length; 	// gives you the number of Setosa data cases.		
******/
public class ScatterPlotCAxis extends ScatterPlotAxis
{  
  private ArrayList<String> uniqueCategories; // every unique category in the order they were added to the Hash
  private String[] allStringElements; // every element in this axis in the same order they were read in.
  private HashMap<String, int[]> categoricalArraysMap;
  
  public ScatterPlotCAxis(String name, int sz)
  {
    super(name, sz);
    values = null;
    uniqueCategories = new ArrayList<String>();
    allStringElements = new String[sz]; // pre-assign a size to the array
    categoricalArraysMap = new HashMap<String, int[]>();  // initialize the hashmap that will store elements based on unique categories.
    type = 1;
    subTicks = 0;
    tickCount = 1;
    range.x = 0.0f;
    range.y = 1.0f;
  }
  
  // In this version of addElement, we keep track of unique instances of each category
  // countainsIndexes is an array that lists out every index that is part of that category
  public void addElement(String e, int index)
  {
    allStringElements[index] = e;
    Object countainsIndexes = categoricalArraysMap.get(e);
    if(countainsIndexes != null) {
      // append the new element to the end of the array:
      int[] tmpCount = (int[])countainsIndexes;
      countainsIndexes = new int[tmpCount.length+1];
      for(int i = 0; i < tmpCount.length; i++){
    	  ((int[])(countainsIndexes))[i] = tmpCount[i];
      }
      ((int[])(countainsIndexes))[tmpCount.length] = index; // do the actual append
      categoricalArraysMap.put(e, (int[])countainsIndexes); // add the array back into the hashmap. (might not be necessary)
    }
    else {
      categoricalArraysMap.put(e, new int[]{index}); // start off a new array for this new category
      uniqueCategories.add(e);
      tickCount++;
      range.y += 1.0;
      filteredRange.y = range.y;
      maxFilter = range.y;
    }
  }
  
  // Used to calculate the horizontal position of a ScatterPlotPoint
  public float getElement(int index)
  {
    // Find which numerical bin the category at the given index fits into:
	String elementCategory = getElementAsString(index);  
	for(int i = 0; i < uniqueCategories.size(); i++)
	{
      if(uniqueCategories.get(i).equals(elementCategory)){
        return i + 1;
      }
    }
    return 0.0f; // if we can't find it, then its just going return zero (this should never happen)
  }
  
  @ Override
  public String getElementAsString(int index)
  {
    return allStringElements[index];
  }
  
  // use this method to access the indexes of data cases within each category
  // Each category stores its indexes in an array within this HashMap
  // use the uniqueCategory array to know what categories exist (each element would be a key to the hashmap)
  // then you can access each array in the HashMap and determine its length or anything else you may want to do.
  public HashMap<String, int[]> getCategoryMap()
  {
    return categoricalArraysMap;
  }
  
  // Use this method to determine what categories exist for this axis
  public ArrayList<String> getUniqueCategories()
  {
	  return uniqueCategories;
  }
  
  public void setUniqueCategories(ArrayList<String> categoriesMap) 
  {
	  uniqueCategories = categoriesMap;
  }
  
  // a utility to check the contents of an axis
  // also a good example of how this data structure works.
  // DO NOT EVER call this every update frame. that would be bad.
  public void printContents()
  {
	  System.out.println("Printing contents of " + label + ":"); 
	  System.out.println("Total of " + allStringElements.length + " elements"); 
	  for(int i = 0; i < uniqueCategories.size(); i++)
	  {
		 int[] pointers = categoricalArraysMap.get(uniqueCategories.get(i));
		 System.out.println(uniqueCategories.get(i) + ": < " + pointers.length + " case(s) >");
		 for(int j = 0; j < pointers.length; j++)
		 {
			 System.out.println("   " + allStringElements[pointers[j]] + "(" + pointers[j] + ")");
		 }
	  }
  }
  // print a range of elements (and dont let them do silly things with the range values)
  public void printContents(int start, int end)
  {
	  for(int i = Math.min(Math.max(0, start), end); i < Math.min(allStringElements.length, end); i++)
	  {
		  System.out.println(allStringElements[i]);
	  }
  }
  
  // DEPRECATED:
  // overrides the default getMark method and returns a string instead
  public String getMark()
  {
	  return "";
  }
}
